package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.es.SkuHasStock;
import com.atguigu.common.to.mq.StockItemDetailTo;
import com.atguigu.common.to.mq.StockItemTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.ware.entity.PurchaseDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.exception.NoStockException;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import com.atguigu.gulimall.ware.vo.SkuWareStock;
import com.atguigu.gulimall.ware.vo.WareSkuLockVo;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.impl.AMQImpl;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

     @Autowired
     WareSkuDao wareSkuDao;

     @Autowired
     ProductFeignService productFeignService;


     @Autowired
     WareOrderTaskDetailService wareOrderTaskDetailService;

     @Autowired
      WareOrderTaskService  wareOrderTaskService;

     @Autowired
     RabbitTemplate rabbitTemplate;


      @Autowired
      OrderFeignService orderFeignService;


      /**
     *    监听
     * @param stockItemTo
     *
     */
      @Override
      public void handleReleaseStock(StockItemTo stockItemTo) throws RuntimeException {
        StockItemDetailTo stockItemDetailTo = stockItemTo.getStockItemDetailTo();
        // 先查询 库存工作单详情是否有数据
        WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(stockItemDetailTo.getId());
        // 如果有数据 说明库存锁定成功   如果没有数据  锁定库存 失败 无需释放库存(回滚就会没有数据)
         if(byId!=null){
             // 锁定成功
             // 获取 工作单 对应的订单编号
             Long taskId = stockItemTo.getTaskId();
             WareOrderTaskEntity wareOrderTask = wareOrderTaskService.getById(taskId);
             String orderSn = wareOrderTask.getOrderSn();

             // 远程调用 订单服务 判断订单状态  如果订单状态为已经取消  或订单不存在 就释放库存
             R r = orderFeignService.getOrderStatusByOrderSn(orderSn);
             WareOrderTaskDetailEntity taskDetail = wareOrderTaskDetailService.getById(byId.getId());
             // 先判断远程调用是否成功
             if(r.getCode()==0){
                 OrderVo data = r.getData(new TypeReference<OrderVo>() {});
                 if(data==null || data.getStatus()==4){
                     // 只有 工作单详情项 的状态 被 锁定
                     if(taskDetail.getStockStatus()==1){
                         // 订单 为空   订单被取消  都要释放库存
                         releaseCurrentItemStock(byId.getSkuId(),byId.getSkuNum(),byId.getWareId(),byId.getId());
                     }


                    // 手动释放 消息
                   //  channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                 }

             }else{
                 // 远程调用失败  拒绝消费消息 重回队列
                 /**   message.getMessageProperties().getDeliveryTag()  消息在消息队列的 标识
                  *     true 让拒绝消费的消息重新回到 队列
                  */
               //  channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
                 throw  new RuntimeException("远程调用失败");
             }

         }else {
             // 锁定失败  库存服务发生了回滚 无序释放库存 直接把该消息 ack
             /**
              *   message.getMessageProperties().getDeliveryTag()  消息在消息队列的标识
              *   false 代表    不需要批量应答
              */
            // channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
         }
    }

    @Override
    public void handleReleaseAfterOrder(String orderSn) {
        //通过 工单表 查出对应的 taskId
        WareOrderTaskEntity order_sn = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));

        // 再通过工单表id 查出所有的相关联 工作单详情 项 也就是所有购物项
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", order_sn.getId()));

        for(WareOrderTaskDetailEntity item:list){
            if(item.getStockStatus()==1){
                WareOrderTaskDetailEntity update = new WareOrderTaskDetailEntity();
                update.setId(item.getId());
                update.setStockStatus(2);
                wareOrderTaskDetailService.updateById(update);
            }
        }

      }

    // 释放 当前 Mq中要处理的 工作单详情项
   @Transactional
    public void releaseCurrentItemStock(Long skuId, Integer skuNum, Long wareId, Long detailId) {
            wareSkuDao.releaseCurrentItemStock(skuId,skuNum,wareId);
       WareOrderTaskDetailEntity detailEntity = wareOrderTaskDetailService.getById(detailId);

       // 如果当前mq处理的 商品 被释放库存 那么对应的工作单详情项 也要修改状态 在此之前还得判断是否 已经被修该过
       if(detailEntity.getStockStatus()==1){
           // 修改状态
           WareOrderTaskDetailEntity detailEntity1 = new WareOrderTaskDetailEntity();
           detailEntity1.setId(detailId);
           detailEntity1.setStockStatus(2);
           wareOrderTaskDetailService.updateById(detailEntity1);
       }

   }







    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        String skuId = (String) params.get("skuId");
        if(!StringUtils.isEmpty(skuId)&& Integer.parseInt(skuId)>0){
            wrapper.eq("sku_id",skuId);
        }

        String wareId = (String) params.get("wareId");
        if(!StringUtils.isEmpty(wareId)&& Integer.parseInt(wareId)>0){
            wrapper.eq("ware_id",wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }


    /**
     * 完成 订单项 进入 库存
     * @param itemId
     */
    @Transactional
    @Override
    public void addStock(PurchaseDetailEntity itemId) {
      // 先做判断 在仓库表中是否有数据
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>()
                .eq("sku_id", itemId.getSkuId())
                .eq("ware_id", itemId.getWareId()));

        if(wareSkuEntities==null|| wareSkuEntities.size()==0){
            WareSkuEntity wareSku=new WareSkuEntity();
            wareSku.setSkuId(itemId.getSkuId());
            wareSku.setStock(itemId.getSkuNum());
            wareSku.setStockLocked(0);
            wareSku.setWareId(itemId.getWareId());
             try {
                 R info = productFeignService.info(itemId.getSkuId());
                 Map<String,Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                 if(info.getCode()==0){
                     wareSku.setSkuName((String) skuInfo.get("skuName"));
                 }
             }catch (Exception e){

             }
             wareSkuDao.insert(wareSku);
        }else{
            wareSkuDao.updateStock(itemId.getWareId(),itemId.getSkuId(),itemId.getSkuNum());
        }


    }

    /**
     * 调用 远程服务 判断是否 有库存
     * @param skuIds
     * @return
     */
    @Override
    public List<SkuHasStock> getSkuHasStock(List<Long> skuIds) {
        List<SkuHasStock> stocks = skuIds.stream().map((item) -> {
            SkuHasStock skuHasStock = new SkuHasStock();
            Long stock = baseMapper.getHasStock(item);
            skuHasStock.setSkuId(item);
            skuHasStock.setHasStock(stock==null?false:stock>0);
            return skuHasStock;
        }).collect(Collectors.toList());

        return stocks;
    }

    @Transactional
    @Override
    public boolean saveOrderToLockStock(WareSkuLockVo wareSkuLockVo) {
        // 锁定库存 第一步 创建工作单
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(wareSkuLockVo.getOrderSn());

         // 保存工作单  会在表中  自动生产工作单 id  这个id 的产生在插入数据库之前  一旦插入成功 就可以直接从 实体类中获取
         wareOrderTaskService.save(taskEntity);


        //先通过 skuId查出  所有 有库存的仓库的id
        List<OrderItemVo> locks = wareSkuLockVo.getLocks();

        List<SkuWareStock> collect = locks.stream().map((vo) -> {
            SkuWareStock skuWareStock = new SkuWareStock();
            skuWareStock.setSkuId(vo.getSkuId());
            List<Long> wareIds = wareSkuDao.getAllWareIdsBySkuId(vo.getSkuId());
            skuWareStock.setWareIds(wareIds);
            skuWareStock.setNum(vo.getCount());
            return skuWareStock;
        }).collect(Collectors.toList());

        boolean allHasLock=true;

        // 开始真正的 锁定库存
        for (SkuWareStock item:collect) {
            boolean skuHasLock=false;
            List<Long> wareIds = item.getWareIds();
            //没有库存 直接抛异常
            if(wareIds==null|| wareIds.size()==0){
             throw new NoStockException("商品没有库存");
            }
            // 对 sku对应有库存的 仓库依次进行 是否能 锁定库存
            for (Long wareId:wareIds) {
                // 这里是执行一个 更新操作  count 为 sql影响的行数  注意这里已经加上了锁  数据库的行锁
                Long count=wareSkuDao.goToLockStock(item.getSkuId(),wareId,item.getNum());
               if(count==1){
                   skuHasLock=true;
                   // 工作单对应的 各个购物项
                   WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(
                           null,
                           item.getSkuId(),
                           null,
                           item.getNum(),
                           taskEntity.getId(),
                           1,
                           wareId
                   );
                   //往相关联的 工作单详情表插入数据
                   wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                   StockItemTo stockItemTo = new StockItemTo();
                   stockItemTo.setTaskId(taskEntity.getId());
                   StockItemDetailTo stockItemDetailTo = new StockItemDetailTo();
                   BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockItemDetailTo);

                   stockItemTo.setStockItemDetailTo(stockItemDetailTo);
                   // 每成功 锁定一个商品的库存 就给mq发消息
                   rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockItemTo);
                   break;
               }
                // 如果 没有等于 1 就继续 下一个仓库判断
            }

             //如果该商品 无法锁定库存 就抛异常  以及回滚之前 锁定 库存的项  所以加 事务
               if(skuHasLock==false){
                   // 抛出对应 sku的 异常
                   throw  new NoStockException(item.getSkuId());
               }

        }
        return allHasLock;
    }

}